﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Golos.Web;

namespace iPoint.Server.Configuration
{
    public class SettingsManager
    {
        public SettingsManager()
        {
            separator = DefaultSeparator;
        }

        public SettingsManager(string separator)
        {
            this.separator = separator;
        }

        private SettingsManager(byte[] data, Encoding encoding, string separator)
        {
            this.separator = separator;
            if (data==null)
                return;
            using (MemoryStream stream = new MemoryStream(data, 0, data.Length))
            {
                using (StreamReader reader = new StreamReader(stream, encoding))
                {
                    while (!reader.EndOfStream)
                    {
                        string line = reader.ReadLine();
                        if (IsEmpty(line)) continue;
                        KeyValuePair<string, string> keyValuePair = GetKeyAndValue(line);
                        if (!settings.ContainsKey(keyValuePair.Key))
                            settings.Add(keyValuePair.Key, new List<string>());
                        settings[keyValuePair.Key].Add(keyValuePair.Value);
                    }
                }
            }
        }

        private SettingsManager(string fileName, Encoding encoding, string separator)
        {
            this.separator = separator;
            this.fileName = fileName;
            if (!File.Exists(fileName))
                return;
            using (StreamReader reader = new StreamReader(fileName, encoding))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    if (IsEmpty(line)) continue;
                    KeyValuePair<string, string> keyValuePair = GetKeyAndValue(line);
                    if (!settings.ContainsKey(keyValuePair.Key))
                        settings.Add(keyValuePair.Key, new List<string>());
                    settings[keyValuePair.Key].Add(keyValuePair.Value);
                }
            }
        }

        public static void CreateSettingsFile(string filePath, Dictionary<string, List<string>> settings)
        {
            using (StreamWriter writer = new StreamWriter(filePath, false))
            {
                foreach (KeyValuePair<string, List<string>> pair in settings)
                    foreach (string value in pair.Value)
                        writer.WriteLine("{0} {1} {2}", pair.Key, DefaultSeparator, value);
                writer.Flush();
            }
        }

        public static SettingsManager ReadFromFile(string fileName)
        {
            return new SettingsManager(fileName, Encoding.UTF8, DefaultSeparator);
        }

        public static SettingsManager ReadFromFile(string fileName, string separator)
        {
            return new SettingsManager(fileName, Encoding.UTF8, separator);
        }

        public static SettingsManager ReadFromFile(string fileName, Encoding encoding)
        {
            return new SettingsManager(fileName, encoding, DefaultSeparator);
        }

        public static SettingsManager ReadFromFile(string fileName, Encoding encoding, string separator)
        {
            return new SettingsManager(fileName, encoding, separator);
        }

        public static SettingsManager ReadFromSettingsFile(string fileName)
        {
            return new SettingsManager(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
                                                          string.Format(@"settings\{0}", fileName)), Encoding.UTF8, DefaultSeparator);
        }

        public void Add(string key, string value)
        {
            if (!settings.ContainsKey(key))
                settings.Add(key, new List<string>());
            settings[key].Add(value);
        }

        public void Add(SettingsManager settingsManager)
        {
            foreach (KeyValuePair<string, List<string>> setting in settingsManager.settings)
            {
                settings.Add(setting.Key, setting.Value);
            }
        }

        public string[] GetAllKeys()
        {
            string[] keys = new string[settings.Count];
            settings.Keys.CopyTo(keys, 0);
            return keys;
        }

        public bool HasParam(string key)
        {
            return settings.ContainsKey(key);
        }

        public List<string> FindAllValues(string key)
        {
            return settings.ContainsKey(key) ? settings[key] : null;
        }

        /// <summary>
        /// Returns last value for the specified key.
        /// </summary>
        /// <exception cref="KeyNotFoundException"></exception>
        public string GetValue(string key)
        {
            if (!settings.ContainsKey(key))
            {
                LogClass.WriteErrorEntry("Parameter with name={0} is absent", key);
                return "0"; //throw new KeyNotFoundException(String.Format("Parameter with name={0} is absent", key));
            }
            List<string> values = settings[key];
            return values[values.Count - 1];
        }

        [Obsolete("Use method FindAllValues", true)]
        public string[] GetValues(string key) { return null; }

        public int GetInt32Value(string key)
        {
            string v = GetValue(key);
            int res;
            if (Int32.TryParse(v, out res))
                return res;
            throw new Exception(String.Format("Error in parsing string {0} to Int32.", v));
        }

        public long GetInt64Value(string key)
        {
            string v = GetValue(key);
            long res;
            if (Int64.TryParse(v, out res))
                return res;
            throw new Exception(String.Format("Error in parsing string {0} to Int64.", v));
        }

        public IPAddress GetIPAddressValue(string key)
        {
            string v = GetValue(key);
            IPAddress res;
            if (IPAddress.TryParse(v, out res))
                return res;
            throw new Exception(String.Format("Error in parsing string {0} to IPAddress.", v));
        }

        public IPAddress GetIPAddressValueOrAny(string key)
        {
            if (!HasParam(key))
                return IPAddress.Any;
            string v = GetValue(key);
            if (v == "0.0.0.0" || string.IsNullOrEmpty(v))
                return IPAddress.Any;
            IPAddress res;
            if (IPAddress.TryParse(v, out res))
                return res;
            throw new Exception(String.Format("Error in parsing string {0} to IPAddress.", v));
        }

        public IPEndPoint GetEndPointValue(string key)
        {
            string[] endpointStrArray = GetValue(key).Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
            IPAddress ipAddress;
            if (!IPAddress.TryParse(endpointStrArray[0], out ipAddress))
            {
                foreach (IPAddress address in Dns.GetHostEntry(endpointStrArray[0]).AddressList)
                {
                    try
                    {
                        if (address.AddressFamily.ToString() != "InterNetwork")
                            continue;
                        IPEndPoint result = new IPEndPoint(address, Convert.ToInt32(endpointStrArray[1]));
                        return result;
                    }
                    catch
                    { continue; }
                }

            }
            return new IPEndPoint(ipAddress, Convert.ToInt32(endpointStrArray[1]));
        }

        private KeyValuePair<string, string> GetKeyAndValue(string line)
        {
            CheckLineFormat(line);
            int i = line.IndexOf(separator);
            string key = line.Substring(0, i).Trim();
            string value = line.Substring(i + separator.Length, line.Length - i - separator.Length).Trim();
            return new KeyValuePair<string, string>(key, value);
        }

        private void CheckLineFormat(string line)
        {
            if (line.IndexOf(separator) == -1)
                throw new Exception(string.Format("Can't find separator '{0}' in config line '{1}'.", separator, line));
        }

        private static bool IsEmpty(string line)
        {
            string t = line.TrimStart(' ', '\t');
            return (string.IsNullOrEmpty(t)) || t.StartsWith("#");
        }

        public string FileName { get { return fileName; } }

        private readonly Dictionary<string, List<string>> settings = new Dictionary<string, List<string>>();
        private string separator;
        private const string DefaultSeparator = "=";
        private string fileName;

        public static SettingsManager ReadFromByteArray(byte[] requestData)
        {
            return new SettingsManager(requestData, Encoding.UTF8, DefaultSeparator);
        }

        internal void write2file(string filePath, string key, string value)
        {
            using (StreamWriter writer = new StreamWriter(filePath, true))
            {
                writer.WriteLine("{0} {1} {2}", key, DefaultSeparator, value);
                //writer.Flush();
            }
        }
    }
}
